package net.risesoft.y9public.service.resource.impl;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import org.apache.commons.lang3.StringUtils;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import lombok.RequiredArgsConstructor;

import net.risesoft.consts.CacheNameConsts;
import net.risesoft.exception.OperationErrorCodeEnum;
import net.risesoft.id.IdType;
import net.risesoft.id.Y9IdGenerator;
import net.risesoft.y9.Y9Context;
import net.risesoft.y9.exception.util.Y9ExceptionUtil;
import net.risesoft.y9.pubsub.event.Y9EntityCreatedEvent;
import net.risesoft.y9.pubsub.event.Y9EntityDeletedEvent;
import net.risesoft.y9.pubsub.event.Y9EntityUpdatedEvent;
import net.risesoft.y9.util.Y9BeanUtil;
import net.risesoft.y9public.entity.resource.Y9App;
import net.risesoft.y9public.entity.resource.Y9Menu;
import net.risesoft.y9public.entity.resource.Y9Operation;
import net.risesoft.y9public.repository.resource.Y9OperationRepository;
import net.risesoft.y9public.service.resource.Y9OperationService;

/**
 * @author dingzhaojun
 * @author qinman
 * @author mengjuhua
 * @date 2022/2/10
 */
@Service
@CacheConfig(cacheNames = CacheNameConsts.RESOURCE_OPERATION)
@Transactional(value = "rsPublicTransactionManager", readOnly = true)
@RequiredArgsConstructor
public class Y9OperationServiceImpl implements Y9OperationService {

    private final Y9OperationRepository y9OperationRepository;

    @Override
    @Transactional(readOnly = false)
    public void delete(List<String> idList) {
        for (String id : idList) {
            this.delete(id);
        }
    }

    @Override
    @CacheEvict(key = "#id")
    @Transactional(readOnly = false)
    public void delete(String id) {
        Y9Operation y9Operation = this.getById(id);
        // 删除关联数据
        Y9Context.publishEvent(new Y9EntityDeletedEvent<>(y9Operation));
        y9OperationRepository.deleteById(id);
    }

    @Transactional(readOnly = false)
    public void deleteByParentId(String parentId) {
        List<Y9Operation> y9OperationList = this.findByParentId(parentId);
        for (Y9Operation y9Operation : y9OperationList) {
            this.delete(y9Operation.getId());
        }
    }

    @Override
    @Transactional(readOnly = false)
    public List<Y9Operation> disable(List<String> idList) {
        List<Y9Operation> y9OperationList = new ArrayList<>();
        for (String id : idList) {
            y9OperationList.add(this.disable(id));
        }
        return y9OperationList;
    }

    @Override
    @CacheEvict(key = "#id")
    @Transactional(readOnly = false)
    public Y9Operation disable(String id) {
        Y9Operation y9Operation = this.getById(id);
        y9Operation.setEnabled(Boolean.FALSE);
        return this.saveOrUpdate(y9Operation);
    }

    @Override
    @Transactional(readOnly = false)
    public List<Y9Operation> enable(List<String> idList) {
        List<Y9Operation> y9OperationList = new ArrayList<>();
        for (String id : idList) {
            y9OperationList.add(this.enable(id));
        }
        return y9OperationList;
    }

    @Override
    @CacheEvict(key = "#id")
    @Transactional(readOnly = false)
    public Y9Operation enable(String id) {
        Y9Operation y9Operation = this.getById(id);
        y9Operation.setEnabled(Boolean.TRUE);
        return this.saveOrUpdate(y9Operation);
    }

    @Override
    public boolean existsById(String id) {
        return y9OperationRepository.existsById(id);
    }

    @Override
    @Cacheable(key = "#id", condition = "#id!=null", unless = "#result==null")
    public Y9Operation findById(String id) {
        return y9OperationRepository.findById(id).orElse(null);
    }

    @Override
    public List<Y9Operation> findByNameLike(String name) {
        return y9OperationRepository.findByNameContainingOrderByTabIndex(name);
    }

    @Override
    public List<Y9Operation> findByParentId(String parentId) {
        return y9OperationRepository.findByParentIdOrderByTabIndex(parentId);
    }

    @Override
    @Cacheable(key = "#id", condition = "#id!=null", unless = "#result==null")
    public Y9Operation getById(String id) {
        return y9OperationRepository.findById(id).orElseThrow(() -> Y9ExceptionUtil.notFoundException(OperationErrorCodeEnum.OPERATION_NOT_FOUND, id));
    }

    @Override
    @CacheEvict(key = "#id")
    @Transactional(readOnly = false)
    public Y9Operation move(String id, String parentId) {
        Y9Operation y9Operation = this.getById(id);
        y9Operation.setParentId(parentId);
        return this.saveOrUpdate(y9Operation);
    }

    @EventListener
    public void onAppDeleted(Y9EntityDeletedEvent<Y9App> event) {
        Y9App entity = event.getEntity();
        deleteByParentId(entity.getId());
    }

    @EventListener
    public void onMenuDeleted(Y9EntityDeletedEvent<Y9Menu> event) {
        Y9Menu entity = event.getEntity();
        deleteByParentId(entity.getId());
    }

    @Override
    @CacheEvict(key = "#y9Operation.id", condition = "#y9Operation.id!=null")
    @Transactional(readOnly = false)
    public Y9Operation saveOrUpdate(Y9Operation y9Operation) {
        if (StringUtils.isNotBlank(y9Operation.getId())) {
            Optional<Y9Operation> y9OperationOptional = y9OperationRepository.findById(y9Operation.getId());
            if (y9OperationOptional.isPresent()) {
                Y9Operation originOperationResource = y9OperationOptional.get();
                Y9Operation updatedOperationResource = new Y9Operation();
                Y9BeanUtil.copyProperties(originOperationResource, updatedOperationResource);
                Y9BeanUtil.copyProperties(y9Operation, updatedOperationResource);
                updatedOperationResource = y9OperationRepository.save(updatedOperationResource);

                Y9Context.publishEvent(new Y9EntityUpdatedEvent<>(originOperationResource, updatedOperationResource));

                return updatedOperationResource;
            }
        } else {
            y9Operation.setId(Y9IdGenerator.genId(IdType.SNOWFLAKE));
        }

        Y9Context.publishEvent(new Y9EntityCreatedEvent<>(y9Operation));

        return y9OperationRepository.save(y9Operation);
    }

    @Override
    @CacheEvict(key = "#id")
    @Transactional(readOnly = false)
    public Y9Operation updateTabIndex(String id, int index) {
        Y9Operation y9Operation = this.getById(id);
        y9Operation.setTabIndex(index);
        return this.saveOrUpdate(y9Operation);
    }

}
